package cn.coisini.service.impl;

import cn.coisini.mapper.AssociateWordsMapper;
import cn.coisini.model.common.dtos.Result;
import cn.coisini.model.common.enums.ResultEnum;
import cn.coisini.model.common.search.Trie;
import cn.coisini.model.dto.AssociateWordsDto;
import cn.coisini.model.dto.UserSearchDto;
import cn.coisini.model.pojo.AssociateWords;
import cn.coisini.service.AssociateWordsService;
import cn.coisini.utils.IdWorker;
import cn.hutool.core.text.CharSequenceUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static cn.coisini.model.common.constants.AdminConstants.ASSOCIATE_LIST;
import static cn.coisini.utils.JwtUtil.getUserIdFromToken;

/**
 * @author xiangshaw
 * Description: 联想词服务实现
 */
@Service
@RequiredArgsConstructor
public class AssociateWordsServiceImpl extends ServiceImpl<AssociateWordsMapper, AssociateWords> implements AssociateWordsService {

    private final StringRedisTemplate redisTemplate;

    /**
     * 获取联想词
     */
    @Override
    public Result<List<AssociateWordsDto>> getAssociateWords(UserSearchDto userSearchDto) {
        String userId = getUserId();
        if (CharSequenceUtil.isBlank(userSearchDto.getKeyword())) {
            // 如果搜索词为空，直接返回空列表
            return Result.ok(new ArrayList<>());
        }
        // 1.从缓存中获取数据
        String associateList = redisTemplate.opsForValue().get(ASSOCIATE_LIST + userId);
        List<AssociateWords> associateWords = List.of();
        // 标识是否需要从数据库中获取数据
        boolean shouldFetchFromDatabase = true;
        if (CharSequenceUtil.isNotBlank(associateList)) {
            // 2.如果缓存中存在数据，解析 JSON 并检查是否为空列表
            associateWords = JSON.parseArray(associateList, AssociateWords.class);
            if (associateWords != null && !associateWords.isEmpty()) {
                shouldFetchFromDatabase = false;
            }
        }
        if (shouldFetchFromDatabase) {
            // 3.如果缓存中不存在数据或数据为空，则从数据库中查询数据，并存储到缓存中
            associateWords = list();
            // 3.1 缓存存储每个用户不一样的搜索词的联想词列表，最多存放一个月
            redisTemplate.opsForValue().set(ASSOCIATE_LIST + userId, JSON.toJSONString(associateWords), 30, TimeUnit.DAYS);
        }
        // 4.构建trie数据结构，从trie中获取数据，封装返回
        Trie trie = new Trie();
        associateWords.forEach(associateWord -> trie.insert(associateWord.getAssociateWord()));
        // 5.从前台获取数据，从trie中获取数据，封装返回
        List<String> start = trie.startWith(userSearchDto.getKeyword());
        List<AssociateWordsDto> resultList = new ArrayList<>();
        start.forEach(s -> {
            AssociateWordsDto associateWord = new AssociateWordsDto();
            associateWord.setAssociateWord(s);
            resultList.add(associateWord);
        });
        // 6.返回数据
        return Result.ok(resultList);
    }

    private String getUserId() {
        return getUserIdFromToken();
    }

    @Override
    public void addAssociateWords(AssociateWordsDto associateWordsDto) {
        String userId = getUserId();
        // 判断是否重复
        boolean exists = lambdaQuery()
                .eq(AssociateWords::getAssociateWord, associateWordsDto.getAssociateWord())
                .exists();
        if (exists) {
            return;
        }
        AssociateWords associateWords = new AssociateWords();
        associateWords.setId(String.valueOf(new IdWorker().nextId()));
        associateWords.setAssociateWord(associateWordsDto.getAssociateWord());
        associateWords.setCreateTime(LocalDateTime.now());
        boolean i = save(associateWords);
        if (i) {
            // 删除缓存
            redisTemplate.delete(ASSOCIATE_LIST + userId);
        }
    }


    @Override
    public Result<ResultEnum> delAssociateWords(AssociateWordsDto associateWordsDto) {
        String userId = getUserId();
        boolean b = lambdaUpdate()
                .eq(AssociateWords::getAssociateWord, associateWordsDto.getAssociateWord())
                .remove();
        if (b) {
            // 删除缓存
            redisTemplate.delete(ASSOCIATE_LIST + userId);
            return Result.ok(ResultEnum.SUCCESS);
        }
        return Result.error(ResultEnum.FAIL);
    }
}
